#region Copyright (c) 2007-2008, Chris Klochek

/********************************************************************************************************************
'
' Copyright (c) 2007-2008, Chris Klochek
' All rights reserved.
' 
' Redistribution and use in source and binary forms, with or without modification, are permitted provided
' that the following conditions are met:
' 
' * Redistributions of source code must retain the above copyright notice, this list of conditions and the
' 	following disclaimer.
' 
' * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
' 	the following disclaimer in the documentation and/or other materials provided with the distribution.
' 
' * Neither the name of the author nor the names of its contributors may be used to endorse or 
' 	promote products derived from this software without specific prior written permission.
' 
' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
' WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
' PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
' ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
' LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
' INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
' OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
' IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'
'*******************************************************************************************************************/

#endregion

using System;
using System.Collections.Generic;
using System.Text;
using SuedeTest.Core;
using System.Threading;
using System.Reflection;
using System.IO;
using System.Windows.Forms;

using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using SuedeTest.Core.Actions;

namespace SuedeTest.ProcessHost {

   static class Program {

      private static Dictionary<string, string> _clArgs = new Dictionary<string, string>();
      private static readonly string ARGUMENT_ERROR = "Incorrect number of args to the SuedeTest host.  This is probably the result of an improper SuedeTest shutdown.  Please rebuild your project's .exe to fix this problem.";
      private static ProcessLink _processLink = null;

      /// <summary>
      /// Mainline: common:
      ///           host.exe c:\foo\bar\programToTest.exe c:\omg\rofl\SuedeTestDirectory ipc://someIpcAddress -libs c:\ftw\lol\AdditionalAssemblies
      /// 
      ///           recorder:
      ///           
      ///           runner:
      ///             -tests test1 test2 test3
      ///             -spec c:\ttyl\ParamaterizationFile
      ///             -testfile c:\qwerty\TestFile.html
      ///     
      /// </summary>
      /// <param name="args"></param>
      [STAThread]
      static int Main(string[] args) {
         
         Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
         AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
         Helper h;
         
         ParseCommandLine(args);

         if (_clArgs.ContainsKey("-ipc")) {
            try {
               _processLink = (ProcessLink)Activator.GetObject(typeof(ProcessLink), _clArgs["-ipc"]);
            } catch (Exception) { }

            if (_processLink == null) {
               return -3;
            }
         } else {
            throw new ArgumentException(ARGUMENT_ERROR);
         }

         if (_clArgs.ContainsKey("-suede") && _clArgs.ContainsKey("-exe") && _clArgs.ContainsKey("-ipc")) {
            if (_clArgs.ContainsKey("-testfile") && _clArgs.ContainsKey("-tests")) {
               //test-runner
               LoadSuedeAssemblies(_clArgs["-suede"]);
               AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

               if (_clArgs.ContainsKey("-spec")) {
                  h = new RunnerHelper(_clArgs["-testfile"], _clArgs["-spec"], _clArgs["-tests"]);
               } else {
                  h = new RunnerHelper(_clArgs["-testfile"], _clArgs["-tests"]);
               }

            } else if (_clArgs.ContainsKey("-testfile") || _clArgs.ContainsKey("-tests")) {
               //bad arguments--we don't have one of the above cl args
               throw new ArgumentException(ARGUMENT_ERROR);
            } else {
               //test-recorder
               LoadSuedeAssemblies(_clArgs["-suede"]);
               AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

               h = new RecorderHelper();
            }

         } else {
            //bad args--we need suede/exe/ipc locations
            throw new ArgumentException(ARGUMENT_ERROR);
         }

         try {
            if (_clArgs.ContainsKey("-libs")) {
               return h.Run(_clArgs["-exe"], _clArgs["-libs"], _processLink);
            } else {
               return h.Run(_clArgs["-exe"], _processLink);
            }
         } catch (Exception e) {
            Log(e.ToString() + Environment.NewLine + (e.InnerException == null ? "" : e.InnerException.ToString()));
         }

         return -2;
      }

      static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
         Log("Unhandled exception encountered: " + e.ExceptionObject.ToString () + Environment.NewLine);
      }

      static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) {
         Log(e.Exception.ToString() + Environment.NewLine + e.Exception.InnerException == null ? "" : e.Exception.InnerException.ToString());
      }

      private static void ParseCommandLine(string[] args) {
         if (args.Length >= 3) {
            _clArgs.Add("-exe", args[0]);
            _clArgs.Add("-suede", args[1]);
            _clArgs.Add("-ipc", args[2]);

            int pos = 3;

            while (pos < args.Length) {
               switch (args[pos]) {
                  case "-tests":
                     pos = ParseTests(args, ++pos);
                     break;

                  case "-libs":
                     pos = ParseAssemblyDir(args, ++pos);
                     break;

                  case "-spec":
                     pos = ParseSpec(args, ++pos);
                     break;

                  case "-testfile":
                     pos = ParseTestFile(args, ++pos);
                     break;

                  default:
                     pos++;
                     break;
               }
            }
         }
      }

      private static int ParseSpec(string[] args, int startPos) {
         _clArgs.Add("-spec", args[startPos]);
         return startPos + 1;
      }

      private static int ParseTestFile(string[] args, int startPos) {
         _clArgs.Add("-testfile", args[startPos]);
         return startPos + 1;
      }

      private static int ParseAssemblyDir(string[] args, int startPos) {
         _clArgs.Add("-libs", args[startPos]);
         return startPos + 1;
      }

      private static int ParseTests(string[] args, int startPos) {
         int pos = startPos;
         string testNames = "";
         //_testNames = new List<string>();

         while (pos < args.Length && !args[pos].StartsWith("-")) {
            //_testNames.Add(args[pos]);
            testNames += args[pos] + " ";
            pos++;
         }
         _clArgs.Add("-tests", testNames.TrimEnd());
         return pos;
      }

      static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
         foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
            if (asm.FullName == args.Name) {
               return asm;
            }
         }

         Log("Looked for, but didn't find " + args.Name);
         return null;
      }

      public static void Log(string s) {
         _processLink.UpdateLog(s);
      }

      /// <summary>
      /// Load all our SuedeTest assemblies needed to actually run this process
      /// </summary>
      /// <param name="dir"></param>
      private static void LoadSuedeAssemblies(string dir) {
         DirectoryInfo dirInf = new DirectoryInfo(dir);

         foreach (FileInfo fileInf in dirInf.GetFiles()) {
            if (fileInf.Extension.ToLower().Equals(".dll")) {
               try {
                  Assembly.LoadFrom(fileInf.FullName);
               } catch (Exception) { }
            }
         }
      }
   }

}

